package cn.feizhu.aimarket.tools;

import cn.feizhu.aimarket.model.domain.Book;
import cn.feizhu.aimarket.model.enums.FileDirBizEnums;
import cn.feizhu.aimarket.service.BookService;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

/**
 * 使用自定义注解定义工具 - 基于MinioUtils的云存储文件操作
 */
@Slf4j
@Component
public class FileOperationTool {

    @Resource
    private MinioUtils minioUtils;

    @Value("${minio.bucketName}")
    private String defaultBucket;

    @Resource
    private BookService bookService;

    /**
     * 从Minio云存储读取文件内容
     */
    @Tool(description = "Read content from file stored in cloud storage")
    public String readFile(@ToolParam(description = "Name of the file to read") String fileName,
                           @ToolParam(description = "Business type of the file directory, you can only use these: book, doc, img, other") String bizType,
                           @ToolParam(description = "appId") Long appId,
                           @ToolParam(description = "userId") Long userId) {
        log.info("开始读取文件 - fileName: {}, bizType: {}, appId: {}, userId: {}", fileName, bizType, appId, userId);

        FileDirBizEnums fileDirBizEnums = FileDirBizEnums.getEnumsByValue(bizType);
        if (fileDirBizEnums == null) {
            log.error("无效的bizType: {}", bizType);
            return "Error: bizType must be one of these: book, doc, img, other";
        }

        // 构建Minio对象路径 (使用/而不是File.separator，因为Minio使用/作为路径分隔符)
        String objectPath = buildMinioObjectPath(fileDirBizEnums, appId, userId, fileName, false);
        log.info("构建的MinIO对象路径: {}", objectPath);

        try {
            log.info("开始检查文件是否存在 - bucket: {}, objectPath: {}", defaultBucket, objectPath);
            long startTime = System.currentTimeMillis();

            // 检查文件是否存在
            boolean exists = minioUtils.isObjectExist(defaultBucket, objectPath);
            long checkTime = System.currentTimeMillis() - startTime;
            log.info("文件存在性检查完成 - 耗时: {}ms, 结果: {}", checkTime, exists);

            if (!exists) {
                log.warn("文件不存在: {}", objectPath);
                return "Error: File not found: " + objectPath;
            }

            log.info("开始从MinIO读取文件内容");
            startTime = System.currentTimeMillis();

            // 从Minio读取文件内容
            InputStream inputStream = minioUtils.getObject(defaultBucket, objectPath);
            long getObjectTime = System.currentTimeMillis() - startTime;
            log.info("获取MinIO对象流完成 - 耗时: {}ms", getObjectTime);

            startTime = System.currentTimeMillis();
            String content = IoUtil.read(inputStream, StandardCharsets.UTF_8);
            long readTime = System.currentTimeMillis() - startTime;
            log.info("读取文件内容完成 - 耗时: {}ms, 内容长度: {}", readTime, content.length());

            inputStream.close();
            log.info("文件读取操作成功完成 - 总路径: {}", objectPath);

            return content;
        } catch (Exception e) {
            log.error("读取文件时发生异常 - objectPath: {}, 异常信息: {}", objectPath, e.getMessage(), e);
            return "Error in reading file from cloud storage: " + objectPath + ", error: " + e.getMessage();
        }
    }

    /**
     * 路径是 book/appid/userid/filename
     * @param isCreate 这个用来区分是否是创建，如果是创建的话会返回一个加了时间戳的新的文件名，避免重名
     * TODO 这里暂时不考虑重名情况，我感觉这个目录设计的话还是app下是用户
     */
    private String buildMinioObjectPath(FileDirBizEnums fileDirBizEnums, Long appId, Long userId, String fileName, boolean isCreate) {
        log.debug("构建MinIO对象路径 - bizType: {}, appId: {}, userId: {}, fileName: {}, isCreate: {}",
                fileDirBizEnums.getValue(), appId, userId, fileName, isCreate);

        if(isCreate) {
            String checkPath = fileDirBizEnums.getValue() + "/" + appId + "/" + userId + "/" + fileName;
            log.debug("检查文件是否存在: {}", checkPath);

            minioUtils.isObjectExist(defaultBucket, checkPath);
            // TODO 记得.需要转义
            String[] split = fileName.split("\\."); //为了避免文件名重复
            fileName = split[0] + DateUtil.now() + "." + split[1];
            log.debug("生成新的文件名以避免重复: {}", fileName);
        }

        String finalPath = fileDirBizEnums.getValue() + "/" + appId + "/" + userId + "/" + fileName;
        log.debug("最终构建的MinIO对象路径: {}", finalPath);

        return finalPath;
    }


    // TODO 这里根据类型进行保存，这里还是直接就是book
    @Tool(description = "Writer content to file")
    public String writeFile(
            @ToolParam(description = "Name of the file to write") String fileName,
            @ToolParam(description = "content to write to the file") String content,
            @ToolParam(description = "Business type of the file directory, you can only use these: book, doc, img, other") String bizType,
            @ToolParam(description = "appId") Long appId,
            @ToolParam(description = "userId") Long userId) {
        log.info("开始写入文件 - fileName: {}, bizType: {}, appId: {}, userId: {}, 内容长度: {}",
                fileName, bizType, appId, userId, content != null ? content.length() : 0);

        // 这里我们直接使用BOOK目录来存储文件,不改了
        FileDirBizEnums fileDirBizEnums = FileDirBizEnums.getEnumsByValue(bizType);
        if (fileDirBizEnums == null) {
            log.error("无效的bizType: {}", bizType);
            return "Error: bizType must be one of these: book, doc, img, other";
        }
        // TODO 这里无法获取userId,估计是不同session了，这里就传参避免复杂设计，后期可以通过工作流上下文实现
        // 构建Minio对象路径 (使用/而不是File.separator，因为Minio使用/作为路径分隔符)
        String filePath = buildMinioObjectPath(fileDirBizEnums, appId, userId, fileName, false);
        log.info("构建的MinIO文件路径: {}", filePath);

        try {
            log.info("开始准备文件上传 - bucket: {}, filePath: {}", defaultBucket, filePath);
            long startTime = System.currentTimeMillis();

            // 将String内容转换为InputStream
            assert content != null;
            InputStream inputStream = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8));
            long prepareTime = System.currentTimeMillis() - startTime;
            log.info("文件内容转换为InputStream完成 - 耗时: {}ms", prepareTime);

            log.info("开始上传文件到MinIO");
            startTime = System.currentTimeMillis();

            // 使用MinioUtils上传文件
            minioUtils.uploadFile(defaultBucket, filePath, inputStream);

            // 保存到数据库
            Book book = new Book();
            book.setAppId(appId);
            book.setTitle(fileName);
            book.setUri(filePath);
            // TODO 书的封面这个需要有最大控制的，因为api限制，然后可能可以异步去调用，加入队列什么的
            book.setUserId(userId);
//            book.getDescription(); // TODO 这个其实可以后期更新或者是手动生成，或者让AI总结
            bookService.save(book);

            long uploadTime = System.currentTimeMillis() - startTime;
            log.info("文件上传到MinIO完成 - 耗时: {}ms, 路径: {}", uploadTime, filePath);

            return "File written successfully to: " + filePath;
        } catch (Exception e) {
            log.error("写入文件时发生异常 - filePath: {}, 异常信息: {}", filePath, e.getMessage(), e);
            return "Error in writing file to: " + filePath + ", Error: " + e.getMessage();
        }
    }


}
